/*
 * Copyright Konstantin Triger <kostat@gmail.com> 
 * 
 * This file is part of Jaque 2 JPA - JAva QUEry 2 JPA library <http://code.google.com/p/jaque/>.
 * 
 * Jaque is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jaque is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package jaque.jpa;

import java.util.*;

import jaque.*;
import jaque.expressions.*;
import jaque.functions.Function;

import javax.persistence.*;
import javax.persistence.Query;

public final class JaqueEntityManager {
	private final EntityManager _em;

	public JaqueEntityManager(EntityManager em) {
		if (em == null)
			throw new NullPointerException("EntityManager");
		_em = em;
	}

	public EntityManager getEntityManager() {
		return _em;
	}

	// public <E> Queryable<E> entity(Class<E> source) {
	// return new JpaQueryable<E>(getEntityManager(), source);
	// }

	public <E> Queryable<E> from(Class<E> source) {
		return new JpaQueryable<E>(getEntityManager(), source);
	}

	private static final class JpaQueryable<E> extends SimpleQueryable<E> {
		// @Override
		// public Iterator<E> iterator() {
		// throw new UnsupportedOperationException();
		// }

		private final EntityManager _em;

		JpaQueryable(EntityManager em, Class<E> source) {
			this(em, source, null);
		}

		JpaQueryable(EntityManager em, Class<E> source, Expression e) {
			super(source, e);
			_em = em;
		}

		public <T> Queryable<T> createQueryable(Class<T> type, Expression e) {
			return new JpaQueryable<T>(_em, type, e);
		}

		@SuppressWarnings("unchecked")
		public Iterable<E> iterable() {
			final Query q = execute();
			return (Iterable<E>) q.getResultList();
			// return new Iterable<E>() {
			// @SuppressWarnings("unchecked")
			// public Iterator<E> iterator() {
			// // TODO apply parameters!
			// return (Iterator<E>)q.getResultList().iterator();
			// }
			// };
		}

		@SuppressWarnings("unchecked")
		public E single() {
			return (E) execute().getSingleResult();
		}

		private Query execute() {
			Expression e = getExpression().apply(
					JPQLFunctionNormalizer.Instance);
			e = QueryBinder.bind(e);
			e = e.apply(SubqueryRemover.Instance);
			e = e.apply(ExpressionNormalizer.Instance);
			// assert (e != null);
			QueryFormatter formatter = new QueryFormatter();
			e.apply(formatter);
			return formatter.createQuery(_em);
		}

		// public <R> Iterable<R> invoke(
		// LambdaExpression<Function<Iterable<R>, Iterable<? extends E>>>
		// lambda) {
		// // InvocationExpression ie = (InvocationExpression)lambda.getBody();
		// List<Expression> args = new ArrayList<Expression>(1);
		// // List<Expression> args = new
		// ArrayList<Expression>(ie.getArguments());
		// // for (int i = 0; i < args.size(); i++)
		// // {
		// // Expression arg = args.get(i);
		// // if (arg.getExpressionType() == ExpressionType.Parameter &&
		// // ((ParameterExpression)arg).getIndex() == 0)
		// // args.set(i, Expression.constant(this, getClass())); //new
		// TableExpression(_source, null)
		// // }
		//			
		// args.add(Expression.constant(this, getClass()));
		//			
		// args = Collections.unmodifiableList(args);
		// // ie = Expression.invoke(ie.getMethod(), args);
		// InvocationExpression ie = Expression.invoke(lambda, args);
		// //ie =
		// (InvocationExpression)ie.apply(LambdaExpressionReducer.Instance);
		// ie = (InvocationExpression)ie.apply(new ExpressionInliner());
		// Expression e = ie.apply(JPQLFunctionNormalizer.Instance);
		// e = QueryBinder.bind(e);
		// e = e.apply(SubqueryRemover.Instance);
		// e = e.apply(ExpressionNormalizer.Instance);
		// //assert (e != null);
		// QueryFormatter formatter = new QueryFormatter();
		// e.apply(formatter);
		// final Query q = formatter.createQuery(_em);
		// return new Iterable<R>() {
		// @SuppressWarnings("unchecked")
		// public Iterator<R> iterator() {
		// // TODO apply parameters!
		// return (Iterator<R>)q.getResultList().iterator();
		// }
		// };
		// }
	}
}
